# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.742+1.737.10.1 -> 1.742.3.1 # arch/i386/kernel/mpparse.c 1.9.2.2 -> 1.9.1.3 # arch/i386/kernel/io_apic.c 1.17.2.2 -> 1.17.1.3 # diff -Nru a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c --- a/arch/i386/kernel/io_apic.c Wed Oct 8 09:07:58 2003 +++ b/arch/i386/kernel/io_apic.c Wed Oct 8 09:07:58 2003 @@ -17,6 +17,7 @@ * thanks to Eric Gilmore * and Rolf G. Tews * for testing these extensively + * Paul Diefenbaugh : Added full ACPI support */ #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include @@ -1052,6 +1054,10 @@ unsigned char old_id; unsigned long flags; + if (acpi_ioapic) + /* This gets done during IOAPIC enumeration for ACPI. */ + return; + if (clustered_apic_mode) /* We don't have a good way to do this yet - hack */ phys_id_present_map = (u_long) 0xf; @@ -1650,8 +1656,7 @@ printk("ENABLING IO-APIC IRQs\n"); /* - * Set up the IO-APIC IRQ routing table by parsing the MP-BIOS - * mptable: + * Set up IO-APIC IRQ routing. */ setup_ioapic_ids_from_mpc(); sync_Arb_IDs(); @@ -1660,3 +1665,159 @@ check_timer(); print_IO_APIC(); } + + +/* -------------------------------------------------------------------------- + ACPI-based IOAPIC Configuration + -------------------------------------------------------------------------- */ + +#ifdef CONFIG_ACPI_BOOT + +#define IO_APIC_MAX_ID 15 + +int __init io_apic_get_unique_id (int ioapic, int apic_id) +{ + struct IO_APIC_reg_00 reg_00; + static unsigned long apic_id_map = 0; + unsigned long flags; + int i = 0; + + /* + * The P4 platform supports up to 256 APIC IDs on two separate APIC + * buses (one for LAPICs, one for IOAPICs), where predecessors only + * supports up to 16 on one shared APIC bus. + * + * TBD: Expand LAPIC/IOAPIC support on P4-class systems to take full + * advantage of new APIC bus architecture. + */ + + if (!apic_id_map) + apic_id_map = phys_cpu_present_map; + + spin_lock_irqsave(&ioapic_lock, flags); + *(int *)®_00 = io_apic_read(ioapic, 0); + spin_unlock_irqrestore(&ioapic_lock, flags); + + if (apic_id >= IO_APIC_MAX_ID) { + printk(KERN_WARNING "IOAPIC[%d]: Invalid apic_id %d, trying " + "%d\n", ioapic, apic_id, reg_00.ID); + apic_id = reg_00.ID; + } + + /* + * Every APIC in a system must have a unique ID or we get lots of nice + * 'stuck on smp_invalidate_needed IPI wait' messages. + */ + if (apic_id_map & (1 << apic_id)) { + + for (i = 0; i < IO_APIC_MAX_ID; i++) { + if (!(apic_id_map & (1 << i))) + break; + } + + if (i == IO_APIC_MAX_ID) + panic("Max apic_id exceeded!\n"); + + printk(KERN_WARNING "IOAPIC[%d]: apic_id %d already used, " + "trying %d\n", ioapic, apic_id, i); + + apic_id = i; + } + + apic_id_map |= (1 << apic_id); + + if (reg_00.ID != apic_id) { + reg_00.ID = apic_id; + + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(ioapic, 0, *(int *)®_00); + *(int *)®_00 = io_apic_read(ioapic, 0); + spin_unlock_irqrestore(&ioapic_lock, flags); + + /* Sanity check */ + if (reg_00.ID != apic_id) + panic("IOAPIC[%d]: Unable change apic_id!\n", ioapic); + } + + printk(KERN_INFO "IOAPIC[%d]: Assigned apic_id %d\n", ioapic, apic_id); + + return apic_id; +} + + +int __init io_apic_get_version (int ioapic) +{ + struct IO_APIC_reg_01 reg_01; + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + *(int *)®_01 = io_apic_read(ioapic, 1); + spin_unlock_irqrestore(&ioapic_lock, flags); + + return reg_01.version; +} + + +int __init io_apic_get_redir_entries (int ioapic) +{ + struct IO_APIC_reg_01 reg_01; + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + *(int *)®_01 = io_apic_read(ioapic, 1); + spin_unlock_irqrestore(&ioapic_lock, flags); + + return reg_01.entries; +} + + +int io_apic_set_pci_routing (int ioapic, int pin, int irq) +{ + struct IO_APIC_route_entry entry; + unsigned long flags; + + if (!IO_APIC_IRQ(irq)) { + printk(KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0/n", + ioapic); + return -EINVAL; + } + + /* + * Generate a PCI IRQ routing entry and program the IOAPIC accordingly. + * Note that we mask (disable) IRQs now -- these get enabled when the + * corresponding device driver registers for this IRQ. + */ + + memset(&entry,0,sizeof(entry)); + + entry.delivery_mode = dest_LowestPrio; + entry.dest_mode = INT_DELIVERY_MODE; + entry.dest.logical.logical_dest = TARGET_CPUS; + entry.mask = 1; /* Disabled (masked) */ + entry.trigger = 1; /* Level sensitive */ + entry.polarity = 1; /* Low active */ + + add_pin_to_irq(irq, ioapic, pin); + + entry.vector = assign_irq_vector(irq); + + printk(KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> " + "IRQ %d)\n", ioapic, + mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq); + + irq_desc[irq].handler = &ioapic_level_irq_type; + + set_intr_gate(entry.vector, interrupt[irq]); + + if (!ioapic && (irq < 16)) + disable_8259A_irq(irq); + + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(ioapic, 0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(ioapic, 0x10+2*pin, *(((int *)&entry)+0)); + spin_unlock_irqrestore(&ioapic_lock, flags); + + return entry.vector; +} + +#endif /*CONFIG_ACPI_BOOT*/ diff -Nru a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c --- a/arch/i386/kernel/mpparse.c Wed Oct 8 09:07:58 2003 +++ b/arch/i386/kernel/mpparse.c Wed Oct 8 09:07:58 2003 @@ -9,12 +9,14 @@ * Erich Boleyn : MP v1.4 and additional changes. * Alan Cox : Added EBDA scanning * Ingo Molnar : various cleanups and rewrites - * Maciej W. Rozycki : Bits for default MP configurations + * Maciej W. Rozycki: Bits for default MP configurations + * Paul Diefenbaugh: Added full ACPI support */ #include #include #include +#include #include #include #include @@ -23,9 +25,11 @@ #include #include +#include #include #include #include +#include #include /* Have we found an MP table */ @@ -127,12 +131,6 @@ return n; } -#ifdef CONFIG_X86_IO_APIC -extern int have_acpi_tables; /* set by acpitable.c */ -#else -#define have_acpi_tables (0) -#endif - /* * Have to match translation table entries to main table entries by counter * hence the mpc_record variable .... can't see a less disgusting way of @@ -226,7 +224,7 @@ if (m->mpc_apicid > MAX_APICS) { printk("Processor #%d INVALID. (Max ID: %d).\n", m->mpc_apicid, MAX_APICS); - --num_processors; + --num_processors; return; } ver = m->mpc_apicver; @@ -433,10 +431,11 @@ printk("APIC at: 0x%lX\n",mpc->mpc_lapic); - /* save the local APIC address, it might be non-default, - * but only if we're not using the ACPI tables + /* + * Save the local APIC address (it might be non-default) -- but only + * if we're not using ACPI. */ - if (!have_acpi_tables) + if (!acpi_lapic) mp_lapic_addr = mpc->mpc_lapic; if ((clustered_apic_mode == CLUSTERED_APIC_NUMAQ) && mpc->mpc_oemptr) { @@ -455,9 +454,8 @@ { struct mpc_config_processor *m= (struct mpc_config_processor *)mpt; - - /* ACPI may already have provided this one for us */ - if (!have_acpi_tables) + /* ACPI may have already provided this data */ + if (!acpi_lapic) MP_processor_info(m); mpt += sizeof(*m); count += sizeof(*m); @@ -676,7 +674,6 @@ } static struct intel_mp_floating *mpf_found; -extern void config_acpi_tables(void); /* * Scan the memory blocks for an SMP configuration block. @@ -685,17 +682,19 @@ { struct intel_mp_floating *mpf = mpf_found; -#ifdef CONFIG_X86_IO_APIC /* - * Check if the ACPI tables are provided. Use them only to get - * the processor information, mainly because it provides - * the info on the logical processor(s), rather than the physical - * processor(s) that are provided by the MPS. We attempt to - * check only if the user provided a commandline override + * ACPI may be used to obtain the entire SMP configuration or just to + * enumerate/configure processors (CONFIG_ACPI_HT_ONLY). Note that + * ACPI supports both logical (e.g. Hyper-Threading) and physical + * processors, where MPS only supports physical. */ - config_acpi_tables(); -#endif - + if (acpi_lapic && acpi_ioapic) { + printk(KERN_INFO "Using ACPI (MADT) for SMP configuration information\n"); + return; + } + else if (acpi_lapic) + printk(KERN_INFO "Using ACPI for processor (LAPIC) configuration information\n"); + printk("Intel MultiProcessor Specification v1.%d\n", mpf->mpf_specification); if (mpf->mpf_feature2 & (1<<7)) { printk(" IMCR and PIC compatibility mode.\n"); @@ -813,11 +812,13 @@ * trustworthy, simply because the SMP table may have been * stomped on during early boot. These loaders are buggy and * should be fixed. + * + * MP1.4 SPEC states to only scan first 1K of 4K EBDA. */ address = *(unsigned short *)phys_to_virt(0x40E); address <<= 4; - smp_scan_config(address, 0x1000); + smp_scan_config(address, 0x400); if (smp_found_config) printk(KERN_WARNING "WARNING: MP table in the EBDA can be UNSAFE, contact linux-smp@vger.kernel.org if you experience SMP problems!\n"); } @@ -856,3 +857,315 @@ #endif } + +/* -------------------------------------------------------------------------- + ACPI-based MP Configuration + -------------------------------------------------------------------------- */ + +#ifdef CONFIG_ACPI_BOOT + +void __init mp_register_lapic_address ( + u64 address) +{ + mp_lapic_addr = (unsigned long) address; + + set_fixmap_nocache(FIX_APIC_BASE, mp_lapic_addr); + + if (boot_cpu_physical_apicid == -1U) + boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID)); + + Dprintk("Boot CPU = %d\n", boot_cpu_physical_apicid); +} + + +void __init mp_register_lapic ( + u8 id, + u8 enabled) +{ + struct mpc_config_processor processor; + int boot_cpu = 0; + + if (id >= MAX_APICS) { + printk(KERN_WARNING "Processor #%d invalid (max %d)\n", + id, MAX_APICS); + return; + } + + if (id == boot_cpu_physical_apicid) + boot_cpu = 1; + + processor.mpc_type = MP_PROCESSOR; + processor.mpc_apicid = id; + processor.mpc_apicver = 0x10; /* TBD: lapic version */ + processor.mpc_cpuflag = (enabled ? CPU_ENABLED : 0); + processor.mpc_cpuflag |= (boot_cpu ? CPU_BOOTPROCESSOR : 0); + processor.mpc_cpufeature = (boot_cpu_data.x86 << 8) | + (boot_cpu_data.x86_model << 4) | boot_cpu_data.x86_mask; + processor.mpc_featureflag = boot_cpu_data.x86_capability[0]; + processor.mpc_reserved[0] = 0; + processor.mpc_reserved[1] = 0; + + MP_processor_info(&processor); +} + +#ifdef CONFIG_X86_IO_APIC + +#define MP_ISA_BUS 0 +#define MP_MAX_IOAPIC_PIN 127 + +struct mp_ioapic_routing { + int apic_id; + int irq_start; + int irq_end; + u32 pin_programmed[4]; +} mp_ioapic_routing[MAX_IO_APICS]; + + +static int __init mp_find_ioapic ( + int irq) +{ + int i = 0; + + /* Find the IOAPIC that manages this IRQ. */ + for (i = 0; i < nr_ioapics; i++) { + if ((irq >= mp_ioapic_routing[i].irq_start) + && (irq <= mp_ioapic_routing[i].irq_end)) + return i; + } + + printk(KERN_ERR "ERROR: Unable to locate IOAPIC for IRQ %d/n", irq); + + return -1; +} + + +void __init mp_register_ioapic ( + u8 id, + u32 address, + u32 irq_base) +{ + int idx = 0; + + if (nr_ioapics >= MAX_IO_APICS) { + printk(KERN_ERR "ERROR: Max # of I/O APICs (%d) exceeded " + "(found %d)\n", MAX_IO_APICS, nr_ioapics); + panic("Recompile kernel with bigger MAX_IO_APICS!\n"); + } + if (!address) { + printk(KERN_ERR "WARNING: Bogus (zero) I/O APIC address" + " found in MADT table, skipping!\n"); + return; + } + + idx = nr_ioapics++; + + mp_ioapics[idx].mpc_type = MP_IOAPIC; + mp_ioapics[idx].mpc_flags = MPC_APIC_USABLE; + mp_ioapics[idx].mpc_apicaddr = address; + + set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address); + mp_ioapics[idx].mpc_apicid = io_apic_get_unique_id(idx, id); + mp_ioapics[idx].mpc_apicver = io_apic_get_version(idx); + + /* + * Build basic IRQ lookup table to facilitate irq->io_apic lookups + * and to prevent reprogramming of IOAPIC pins (PCI IRQs). + */ + mp_ioapic_routing[idx].apic_id = mp_ioapics[idx].mpc_apicid; + mp_ioapic_routing[idx].irq_start = irq_base; + mp_ioapic_routing[idx].irq_end = irq_base + + io_apic_get_redir_entries(idx); + + printk("IOAPIC[%d]: apic_id %d, version %d, address 0x%lx, " + "IRQ %d-%d\n", idx, mp_ioapics[idx].mpc_apicid, + mp_ioapics[idx].mpc_apicver, mp_ioapics[idx].mpc_apicaddr, + mp_ioapic_routing[idx].irq_start, + mp_ioapic_routing[idx].irq_end); + + return; +} + + +void __init mp_override_legacy_irq ( + u8 bus_irq, + u8 polarity, + u8 trigger, + u32 global_irq) +{ + struct mpc_config_intsrc intsrc; + int i = 0; + int found = 0; + int ioapic = -1; + int pin = -1; + + /* + * Convert 'global_irq' to 'ioapic.pin'. + */ + ioapic = mp_find_ioapic(global_irq); + if (ioapic < 0) + return; + pin = global_irq - mp_ioapic_routing[ioapic].irq_start; + + /* + * TBD: This check is for faulty timer entries, where the override + * erroneously sets the trigger to level, resulting in a HUGE + * increase of timer interrupts! + */ + if ((bus_irq == 0) && (global_irq == 2) && (trigger == 3)) + trigger = 1; + + intsrc.mpc_type = MP_INTSRC; + intsrc.mpc_irqtype = mp_INT; + intsrc.mpc_irqflag = (trigger << 2) | polarity; + intsrc.mpc_srcbus = MP_ISA_BUS; + intsrc.mpc_srcbusirq = bus_irq; /* IRQ */ + intsrc.mpc_dstapic = mp_ioapics[ioapic].mpc_apicid; /* APIC ID */ + intsrc.mpc_dstirq = pin; /* INTIN# */ + + Dprintk("Int: type %d, pol %d, trig %d, bus %d, irq %d, %d-%d\n", + intsrc.mpc_irqtype, intsrc.mpc_irqflag & 3, + (intsrc.mpc_irqflag >> 2) & 3, intsrc.mpc_srcbus, + intsrc.mpc_srcbusirq, intsrc.mpc_dstapic, intsrc.mpc_dstirq); + + /* + * If an existing [IOAPIC.PIN -> IRQ] routing entry exists we override it. + * Otherwise create a new entry (e.g. global_irq == 2). + */ + for (i = 0; i < mp_irq_entries; i++) { + if ((mp_irqs[i].mpc_dstapic == intsrc.mpc_dstapic) + && (mp_irqs[i].mpc_dstirq == intsrc.mpc_dstirq)) { + mp_irqs[i] = intsrc; + found = 1; + break; + } + } + if (!found) { + mp_irqs[mp_irq_entries] = intsrc; + if (++mp_irq_entries == MAX_IRQ_SOURCES) + panic("Max # of irq sources exceeded!\n"); + } + + return; +} + + +void __init mp_config_acpi_legacy_irqs (void) +{ + struct mpc_config_intsrc intsrc; + int i = 0; + int ioapic = -1; + + /* + * Fabricate the legacy ISA bus (bus #31). + */ + mp_bus_id_to_type[MP_ISA_BUS] = MP_BUS_ISA; + Dprintk("Bus #%d is ISA\n", MP_ISA_BUS); + + /* + * Locate the IOAPIC that manages the ISA IRQs (0-15). + */ + ioapic = mp_find_ioapic(0); + if (ioapic < 0) + return; + + intsrc.mpc_type = MP_INTSRC; + intsrc.mpc_irqflag = 0; /* Conforming */ + intsrc.mpc_srcbus = MP_ISA_BUS; + intsrc.mpc_dstapic = mp_ioapics[ioapic].mpc_apicid; + + /* + * Use the default configuration for the IRQs 0-15. These may be + * overriden by (MADT) interrupt source override entries. + */ + for (i = 0; i < 16; i++) { + + if (i == 2) continue; /* Don't connect IRQ2 */ + + intsrc.mpc_irqtype = i ? mp_INT : mp_ExtINT; /* 8259A to #0 */ + intsrc.mpc_srcbusirq = i; /* Identity mapped */ + intsrc.mpc_dstirq = i; + + Dprintk("Int: type %d, pol %d, trig %d, bus %d, irq %d, " + "%d-%d\n", intsrc.mpc_irqtype, intsrc.mpc_irqflag & 3, + (intsrc.mpc_irqflag >> 2) & 3, intsrc.mpc_srcbus, + intsrc.mpc_srcbusirq, intsrc.mpc_dstapic, + intsrc.mpc_dstirq); + + mp_irqs[mp_irq_entries] = intsrc; + if (++mp_irq_entries == MAX_IRQ_SOURCES) + panic("Max # of irq sources exceeded!\n"); + } + + return; +} + +#endif /*CONFIG_X86_IO_APIC*/ + +#ifdef CONFIG_ACPI_PCI + +void __init mp_parse_prt (void) +{ + struct list_head *node = NULL; + struct acpi_prt_entry *entry = NULL; + int vector = 0; + int ioapic = -1; + int ioapic_pin = 0; + int irq = 0; + int idx, bit = 0; + + /* + * Parsing through the PCI Interrupt Routing Table (PRT) and program + * routing for all static (IOAPIC-direct) entries. + */ + list_for_each(node, &acpi_prt.entries) { + entry = list_entry(node, struct acpi_prt_entry, node); + + /* We're only interested in static (non-link) entries. */ + if (entry->link.handle) + continue; + + irq = entry->link.index; + ioapic = mp_find_ioapic(irq); + if (ioapic < 0) + continue; + ioapic_pin = irq - mp_ioapic_routing[ioapic].irq_start; + + /* + * Avoid pin reprogramming. PRTs typically include entries + * with redundant pin->irq mappings (but unique PCI devices); + * we only only program the IOAPIC on the first. + */ + bit = ioapic_pin % 32; + idx = (ioapic_pin < 32) ? 0 : (ioapic_pin / 32); + if (idx > 3) { + printk(KERN_ERR "Invalid reference to IOAPIC pin " + "%d-%d\n", mp_ioapic_routing[ioapic].apic_id, + ioapic_pin); + continue; + } + if ((1<irq = irq; + continue; + } + + mp_ioapic_routing[ioapic].pin_programmed[idx] |= (1<irq = irq; + + printk(KERN_DEBUG "%02x:%02x:%02x[%c] -> %d-%d -> vector 0x%02x" + " -> IRQ %d\n", entry->id.segment, entry->id.bus, + entry->id.device, ('A' + entry->pin), + mp_ioapic_routing[ioapic].apic_id, ioapic_pin, vector, + entry->irq); + } + + return; +} + +#endif /*CONFIG_ACPI_PCI*/ + +#endif /*CONFIG_ACPI_BOOT*/